/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2020年9月3日
 */
package com.massyframework.beanskin.runtime.modular.assembly.handling;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import org.slf4j.Logger;

import com.massyframework.beanskin.ModuleRuntimeException;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyContext;
import com.massyframework.beanskin.assembly.beans.BeanNotFoundException;
import com.massyframework.beanskin.assembly.handling.HandlerBase;
import com.massyframework.beanskin.assembly.handling.LazyInitHandler;
import com.massyframework.beanskin.assembly.handling.LifecycleEventAdapter;
import com.massyframework.beanskin.assembly.handling.LifecycleManager;
import com.massyframework.beanskin.service.ExportServiceDescriptor;
import com.massyframework.beanskin.service.ServiceFactory;
import com.massyframework.beanskin.service.ServiceNotFoundException;
import com.massyframework.beanskin.service.ServiceRegistration;
import com.massyframework.beanskin.service.ServiceRegistry;
import com.massyframework.beanskin.service.ServiceRepository;
import com.massyframework.beanskin.util.exceptioncollect.ExceptionCollectUtils;

/**
 * 在装配件启动后，向运行框架注册输出服务。
 * 
 * @author huangkh
 */
class ServiceExporter extends HandlerBase {

	private ListenerImpl listener;

    public ServiceExporter() {
    }

    /**
     * 初始化
     *
     * @throws Exception 装配件初始化抛出的例外
     */
    @Override
    protected synchronized void init() throws Exception {
        super.init();
        if (this.listener == null){
            this.listener = new ListenerImpl();
            LifecycleManager handler = this.getLifecycleManager();
            handler.addListener(this.listener);
        }
    }

    /**
     * 释放
     */
    @Override
    public synchronized void destroy() {
        if (this.listener != null){
            LifecycleManager handler = this.getLifecycleManager();
            handler.removeListener(this.listener);
            this.listener = null;
        }
        super.destroy();
    }

    private class ListenerImpl extends LifecycleEventAdapter {

        private List<ServiceRegistration<?>> registrations;

        public ListenerImpl(){
            this.registrations = new ArrayList<ServiceRegistration<?>>();
        }

        /**
         * 已激活，装配件进入工作状态
         */
        @Override
        public synchronized void onActivated() {
            super.onActivated();

            Assembly assembly = getAssociatedAssembly();
            boolean lazyInit = this.isLazyInit(assembly);

            ServiceRepository repository =
                    ServiceRepository.retrieveFrom(getAssociatedAssembly());
            ServiceRegistry registry =
                    repository.getServiceRegistry();
            AssemblyContext context =
                    getHandler(AssemblyContext.class);
            List<ExportServiceDescriptor> descriptors=
                    assembly.getConfig().getExportServiceDescriptors();

            try {
                for (ExportServiceDescriptor descriptor : descriptors) {
                    Class<?>[] types = descriptor.getExportTypes();
                    boolean isInterfaces = this.isInterfaces(types);

                    String beanName = descriptor.getBeanName();
                    if (beanName == null) {
                        beanName = types[0].getName();
                    }

                    Object service = null;
                    if (lazyInit && isInterfaces){
                    	Class<?> beanType = context.getBeanType(beanName);
                    	
                    	Set<Class<?>> interfaceTypes = new HashSet<>();
                    	if (beanType != null) {
	                    	this.retrieveInterfaces(beanType, classes -> {
	                    		for (Class<?> clazz: classes) {
	                    			interfaceTypes.add(clazz);
	                    		}
	                    	});
                    	}
                    	
                        if (!interfaceTypes.isEmpty()){
                            boolean contains = this.containsExportTypes(interfaceTypes, types);
                            try {
                                service = contains ?
                                            ServiceProxyFactory.createProxy(beanName,
                                                interfaceTypes.toArray(new Class<?>[interfaceTypes.size()]), context):
                                            ServiceProxyFactory.createProxy(beanName, types, context);
                                Logger logger = getLogger();
                                if (logger.isDebugEnabled()) {
                                	logger.debug("create proxy service: beanName=".concat(beanName).concat("."));
                                }
                            }catch(Throwable e){
                                e.printStackTrace();
                            }
                        }
                    }

                    if (service == null){
                        service = context.getBean(beanName);
                    }

                    if (ServiceFactory.class.isAssignableFrom(service.getClass())) {
                        ServiceFactory<?> factory = ServiceFactory.class.cast(service);
                        this.registrations.add(
                                registry.register(
                                        descriptor.getExportTypes(),
                                        factory,
                                        descriptor.getServiceProperties()));
                    } else {
                        this.registrations.add(
                                registry.register(
                                		descriptor.getExportTypes(),
                                        service,
                                        descriptor.getServiceProperties()));
                    }
                }
            }catch(BeanNotFoundException e) {
            	ServiceNotFoundException ex = 
            			new ServiceNotFoundException("export serivce failed, because cannot found bean: name = \"" + e.getBeanName() + "\".");
            	ExceptionCollectUtils.addException(
            			new ModuleRuntimeException(ServiceExporter.this.getAssociatedAssembly(), ex));
            	throw ex;
            }catch(Exception e) {
            	ExceptionCollectUtils.addException(
            			new ModuleRuntimeException(ServiceExporter.this.getAssociatedAssembly(), e));
                throw e;
            }
        }
        
        /**
         * 取回所有接口类型
         * @param clazz {@link Class}
         * @param consumer {@link Consumer}
         */
        protected void retrieveInterfaces(Class<?> clazz, Consumer<Class<?>[]> consumer){
        	if (clazz == null) return;
        	Class<?>[] interfaceTypes = clazz.getInterfaces();
        	consumer.accept(interfaceTypes);
        	
        	Class<?> parent = clazz.getSuperclass();
        	if (parent != Object.class ) {
        		this.retrieveInterfaces(parent,  consumer);
        	}
        }

        /**
         * 正在钝化，装配件准备退出工作状态
         */
        @Override
        public synchronized void onInactivating() {
            try {
                if (!this.registrations.isEmpty()){
                    for (ServiceRegistration<?> registration: this.registrations){
                        registration.unregister();
                    }
                    this.registrations.clear();
                }
            }catch(Exception e) {
                throw e;
            }finally {
            }
            super.onInactivating();
        }

        /**
         * 是否为懒加载
         * @param assembly {@link Assembly}
         * @return {@link boolean}
         */
        protected boolean isLazyInit(Assembly assembly){
            LazyInitHandler handler = findHandler(LazyInitHandler.class);
            return handler == null ? false: handler.isLazyInit();
        }

        /**
         * 判断{@code serviceTypes}是否都为接口
         * @param serviceTypes {@link Class}数组
         * @return {@link boolean}
         */
        protected boolean isInterfaces(Class<?>[] serviceTypes){
            for (Class<?> serviceType: serviceTypes){
                if (!serviceType.isInterface()){
                    return false;
                }
            }
            return true;
        }

        /**
         * 判断接口类型是否包括所有的输出服务类型
         * @param interfaceTypes {@link Set}
         * @param exportTypes {@link Class}数组
         * @return {@link boolean}
         */
        protected boolean containsExportTypes(Set<Class<?>> interfaceTypes, Class<?>[] exportTypes){
            for (Class<?> exportType: exportTypes){
                if (!interfaceTypes.contains(exportType)){
                    return false;
                }
            }

            return true;
        }

    }

}
